#include <iostream>
#include <fstream>
#include <queue>

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#include "TextMiningCompiler.hh"

const Trie build_trie(const char* path)
{
  Trie trie('\0', 0);

  std::ifstream ifs(path);

  unsigned i = 0;

  while ( ifs.good() && i >= 0 )
    {
      ++i;

      std::string word;
      int frequency;

      ifs >> word;
      ifs >> frequency;

      if (word.length() > 0)
	{
	  trie.add_word(word.c_str(), frequency);
	}
    }

  return trie;
}

OptimizedTrie* build_optimized_trie(const Trie& trie,
				    unsigned &out_size)
{
  out_size = trie.size();

  OptimizedTrie* ot_head = new OptimizedTrie[out_size];
  OptimizedTrie* ot = ot_head;

  unsigned nb_brothers = 1;
  unsigned nb_brothers_next_depth = 0;
  unsigned nb_past_brothers = 0;

  std::queue<const Trie*> fifo;

  fifo.push(&trie);
  fifo.push(NULL);

  while (!fifo.empty())
    {
      const Trie* node = fifo.front();
      fifo.pop();

      if (node == NULL)
	{
	  nb_past_brothers = 0;
	  nb_brothers = nb_brothers_next_depth;
	  nb_brothers_next_depth = 0;
	  
	  if (!fifo.empty())
	    fifo.push(NULL);
	}
      else
	{
	  {
	    ot->value		= node->get_value();
	    ot->frequency	= node->get_frequency();
	    ot->nb_sons		= node->get_nb_sons();
	    ot->first_son_delta = (nb_brothers_next_depth
				   + nb_brothers
				   - nb_past_brothers);
	    ++ot;
	  }

	  const std::list<Trie*>* sons = node->get_sons();
	  for (std::list<Trie*>::const_iterator it = sons->begin();
	       it != sons->end(); ++it)
	    {
	      fifo.push(*it);
	      ++nb_brothers_next_depth;
	    }

	  ++nb_past_brothers;
	}
    }

  out_size = ot - ot_head;

  return ot_head;
}

int main(int argc, char* argv[])
{
  if (argc != 3)
    {
      std::cout << "usage: "
		<< "./TextMiningCompiler "
		<< "/path/to/dict.txt "
		<< "/path/to/dict.bin" << std::endl;
      return 0;
    }

  std::cout << "building dynamic trie... ";

  const Trie trie = build_trie(argv[1]);

  std::cout << "done." << std::endl;

  std::cout << "building static trie... ";

  unsigned size;

  OptimizedTrie* ot = build_optimized_trie(trie, size);

  std::cout << "done." << std::endl;

  std::cout << "writing " << argv[2] << "... ";

  std::ofstream ofs(argv[2], std::ofstream::out | std::ofstream::binary);

  ofs.write((char*) ot, size * sizeof (OptimizedTrie));

  std::cout << "done." << std::endl;

  delete[] ot;

  return 0;
}
